/*
 * Copyright (c) 2008, Solido Systems
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of Solido Systems nor the names of its contributors may be
 * used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.solidosystems.objectkitchen.storage;

import java.io.*;
import java.util.*;
import javax.script.*;
import com.solidosystems.objectkitchen.data.*;

public class BlockSet implements Storage{
	private final static int BLOCKCOUNT=64;
	private Map<Integer,Block> blockHash;
	private String location;
	
	public BlockSet(String location) throws IOException{
		if(!location.endsWith(File.separator))location=location+File.separator;
		this.location=location;
		blockHash=new HashMap<Integer,Block>();
		for(int i=0;i<BLOCKCOUNT;i++){
			File ftest=new File(location+"block_"+i+File.separator);
			if(!ftest.exists())ftest.mkdir();
			Block blk=new Block(location+"block_"+i+File.separator,i);
			blockHash.put(i,blk);
		}
	}
	
	public Map<String,Long> getCounters(){
		HashMap<String,Long> map=new HashMap<String,Long>();
		for(Block blk:blockHash.values()){
			Map<String,Long> cnt=blk.getCounters();
			for(String key:cnt.keySet()){
				if(map.containsKey(key)){
					map.put(key,map.get(key)+cnt.get(key));
				}else{
					map.put(key,cnt.get(key));
				}
			}
		}
		return map;
	}
	
	public List<String> getGroupMembers(String group) throws IOException{
		List<String> result=new ArrayList<String>();
		for(Block blk:blockHash.values()){
			result.addAll(blk.getGroupMembers(group));
		}
		return result;
	}
	
	private Block getBlock(String id){
		return blockHash.get(Math.abs(id.hashCode()) % BLOCKCOUNT);
	}
	
	public byte[] getAttachment(String id,String title) throws IOException{
		Block blk=getBlock(id);
		return blk.getAttachment(id,title);
	}
	
	public byte[] getAttachment(Document doc,String title) throws IOException{
		Block blk=getBlock(doc.getId());
		return blk.getAttachment(doc,title);
	}
	
	public void putAttachment(String id,String title,byte[] data) throws IOException{
		Block blk=getBlock(id);
		blk.putAttachment(id,title,data);
	}
	
	public void deleteAttachment(String id,String title) throws IOException{
		Block blk=getBlock(id);
		blk.deleteAttachment(id,title);
	}
	
	public void executeDocumentUpdate(String id, String engineName, String script) throws IOException,ScriptException{
		Block blk=getBlock(id);
		blk.executeDocumentUpdate(id,engineName,script);
	}
	
	public Document executeDocumentQuery(String id, String engineName, String script) throws IOException,ScriptException{
		Block blk=getBlock(id);
		return blk.executeDocumentQuery(id,engineName,script);
	}
	
	public void executeUpdate(String group, String engineName, String script) throws IOException,ScriptException{
		for(Block blk:blockHash.values()){
			List<String> members=blk.getGroupMembers(group);
			for(String id:members){
				blk.executeDocumentUpdate(id,engineName,script);
			}
		}
	}
	
	public List<Document> executeQuery(String group, String engineName, String script) throws IOException,ScriptException{
		List<Document> result=new ArrayList<Document>();
		for(Block blk:blockHash.values()){
			List<String> members=blk.getGroupMembers(group);
			for(String id:members){
				Document doc=blk.executeDocumentQuery(id,engineName,script);
				if(doc!=null)result.add(doc);
			}
		}
		return result;
	}
	
	public void putDocument(Document doc) throws IOException{
		Block blk=getBlock(doc.getId());
		blk.putDocument(doc);
	}
	
	public boolean hasDocument(String id) throws IOException{
		Block blk=getBlock(id);
		return blk.hasDocument(id);
	}
	
	public Document getOrCreateDocument(String id) throws IOException{
		Block blk=getBlock(id);
		return blk.getOrCreateDocument(id);
	}
	
	public Document getDocument(String id) throws IOException{
		Block blk=getBlock(id);
		return blk.getDocument(id);
	}
	
	public Document getDocument(String id,int version) throws IOException{
		Block blk=getBlock(id);
		return blk.getDocument(id,version);
	}
	
/*	public List<Document> queryDocuments(Predicate where) throws IOException{
		List<Document> result=new ArrayList<Document>();
		for(Block blk:blockHash.values()){
			result.addAll(blk.queryDocuments(where));
		}
		return result;
	}*/
	
	public void deleteDocument(String id) throws IOException{
		Block blk=getBlock(id);
		blk.deleteDocument(id);
	}
}